﻿using System;
using Moq;
using Myotragus.Data.Containers;
using Myotragus.Data.Criteria;
using Myotragus.Data.Repositories;
using Myotragus.Data.Tests.Domain;
using NHibernate;
using NUnit.Framework;

namespace Myotragus.Data.Tests.Tests.Repository
{
	[TestFixture]
	public class ReadRepositoryFixtures
	{
		[Test]
		public void FindByIdAndExists()
		{
			var entities = new Mock<IEntitiesContainer>();
			var queryBuilder = new Mock<IReadQueryBuilder>();
			var result = new Mock<ITestEntity>();
			var resultType = result.Object.GetType();

			entities.Setup(m => m.ResolveType(typeof(ITestEntity))).Returns(resultType);

			queryBuilder.Setup(m => m.FindById(resultType, 1)).Returns(result.Object);
			queryBuilder.Setup(m => m.FindById(resultType, 2))
				.Throws<IndexOutOfRangeException>();

			queryBuilder.Setup(m => m.Exists(resultType, 1)).Returns(true);
			queryBuilder.Setup(m => m.Exists(resultType, 2)).Returns(false);

			var repo = new ReadRepository<ITestEntity, int>(entities.Object, queryBuilder.Object);

			repo.FindById(1);
			Assert.Throws<IndexOutOfRangeException>(() => repo.FindById(2));

			Assert.IsTrue(repo.Exists(1));
			Assert.IsFalse(repo.Exists(2));

			entities.VerifyAll();
			queryBuilder.VerifyAll();
		}

		[Test]
		public void FindAndCountAll()
		{
			var entities = new Mock<IEntitiesContainer>();
			var queryBuilder = new Mock<IReadQueryBuilder>();
			var result = new Mock<ITestEntity>();
			var resultType = result.Object.GetType();
			var criteria = new Mock<ICriteria>().Object;
			var query = new Mock<IPagedQuery<ITestEntity>>().Object;

			entities.Setup(m => m.ResolveType(typeof(ITestEntity))).Returns(resultType);

			queryBuilder.Setup(m => m.CreateCriteria(resultType)).Returns(criteria);
			queryBuilder.Setup(m => m.CreateQuery<ITestEntity>(criteria)).Returns(query);
			queryBuilder.Setup(m => m.Count(criteria)).Returns(10);

			var repo = new ReadRepository<ITestEntity, int>(entities.Object, queryBuilder.Object);

			Assert.AreEqual(query, repo.FindAll());
			Assert.AreEqual(10, repo.CountAll());
			Assert.AreNotEqual(11, repo.CountAll());

			entities.VerifyAll();
			queryBuilder.VerifyAll();
		}

		[Test]
		public void FindAndCountMatching()
		{
			var entities = new Mock<IEntitiesContainer>();
			var queryBuilder = new Mock<IReadQueryBuilder>();
			var result = new Mock<ITestEntity>();
			var resultType = result.Object.GetType();
			var criteria = new Mock<ICriteria>().Object;
			var query = new Mock<IPagedQuery<ITestEntity>>().Object;
			var pattern = new { T1 = 1, T2 = 2 };

			entities.Setup(m => m.ResolveType(typeof(ITestEntity))).Returns(resultType);

			queryBuilder.Setup(m => m.CreateMatchingCriteria(resultType, pattern)).Returns(criteria);
			queryBuilder.Setup(m => m.CreateQuery<ITestEntity>(criteria)).Returns(query);
			queryBuilder.Setup(m => m.Count(criteria)).Returns(10);

			var repo = new ReadRepository<ITestEntity, int>(entities.Object, queryBuilder.Object);

			Assert.AreEqual(query, repo.FindMatching(pattern));
			Assert.AreEqual(10, repo.CountMatching(pattern));
			Assert.AreNotEqual(11, repo.CountMatching(pattern));

			entities.VerifyAll();
			queryBuilder.VerifyAll();
		}

		[Test]
		public void FindAndCountLike()
		{
			var entities = new Mock<IEntitiesContainer>();
			var queryBuilder = new Mock<IReadQueryBuilder>();
			var result = new Mock<ITestEntity>();
			var resultType = result.Object.GetType();
			var criteria = new Mock<ICriteria>().Object;
			var query = new Mock<IPagedQuery<ITestEntity>>().Object;
			var pattern = new { T1 = 1, T2 = 2 };

			entities.Setup(m => m.ResolveType(typeof(ITestEntity))).Returns(resultType);

			queryBuilder.Setup(m => m.CreateLikeCriteria(resultType, pattern)).Returns(criteria);
			queryBuilder.Setup(m => m.CreateQuery<ITestEntity>(criteria)).Returns(query);
			queryBuilder.Setup(m => m.Count(criteria)).Returns(10);

			var repo = new ReadRepository<ITestEntity, int>(entities.Object, queryBuilder.Object);

			Assert.AreEqual(query, repo.FindLike(pattern));
			Assert.AreEqual(10, repo.CountLike(pattern));
			Assert.AreNotEqual(11, repo.CountLike(pattern));

			entities.VerifyAll();
			queryBuilder.VerifyAll();
		}

		[Test]
		public void GuardClauses()
		{
			var entities = new Mock<IEntitiesContainer>();

			Assert.Throws<ArgumentNullException>(() =>
				new ReadRepository<ITestEntity, int>(null, null), "entities");
			Assert.Throws<ArgumentNullException>(() =>
				new ReadRepository<ITestEntity, int>(entities.Object, null), "queryBuilder");
		}
	}
}